/*
 * Copyright (c) Huawei Device Co., Ltd. 2023. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef SECURE_ELEMENT_AGENT_H
#define SECURE_ELEMENT_AGENT_H

#include <map>
#include <mutex>
#include <string>
#include <vector>
#include "./access_rule/access_control_enforcer.h"
#include "channel_impl.h"
#include "iremote_object.h"
#include "v1_0/isecure_element_interface.h"
#include "v1_0/secure_element_types.h"
#include "v1_0/isecure_element_callback.h"
#include "sim_secure_element/v1_0/isecure_element_interface.h"
#include "sim_secure_element/v1_0/secure_element_types.h"
#include "sim_secure_element/v1_0/isecure_element_callback.h"

using EseISecureElementCallback = OHOS::HDI::SecureElement::V1_0::ISecureElementCallback;
using EseSecureElementStatus = OHOS::HDI::SecureElement::V1_0::SecureElementStatus;
using ISeEseHdiV1_0 = OHOS::HDI::SecureElement::V1_0::ISecureElementInterface;

using SimISecureElementCallback = OHOS::HDI::SecureElement::SimSecureElement::V1_0::ISecureElementCallback;
using SimSecureElementStatus = OHOS::HDI::SecureElement::SimSecureElement::V1_0::SecureElementStatus;
using ISeSimHdiV1_0 = OHOS::HDI::SecureElement::SimSecureElement::V1_0::ISecureElementInterface;

namespace OHOS {
namespace SE {
class ChannelImpl;
class SecureElementAgent final : public std::enable_shared_from_this<SecureElementAgent> {
public:
    explicit SecureElementAgent(std::string name);
    ~SecureElementAgent();

    /**
     * @brief Call HDF to init the secure element.
     * @return The status from hdf, init success returns true, otherwise false.
     */
    bool Initialize();

    /**
     * @brief Get the name for this secure element, eSE or SIM.
     * @return The name of this secure element
     */
    std::string GetName();

    /**
     * @brief Call HDF to check secure element present or not.
     * @return True if secure element present, otherwise false.
     */
    bool IsSecureElementPresent();

    /**
     * @brief Call HDF to get the ART of this secure element.
     * @return The ATR of this secure element
     */
    std::string GetAtr();

    /**
     * @brief Get access to the basic channel, as defined in [ISO 7816-4] (the one that has number 0). The obtained
     * object is an instance of the channel class.
     * @param aid The AID of the applet to be selected on this channel, as a byte array, or Null if no applet is to be
     * selected.
     * @param p2 The P2 parameter of the SELECT APDU executed on this channel.
     * @param apduResp The APDU response received.
     * @return An instance of channel if available or Null.
     */
    sptr<ChannelImpl> OpenBasicChannel(const std::string &aid, uint8_t p2, std::string &response);
    sptr<ChannelImpl> OpenBasicChannelWithoutChannelAccess(const std::string &aid, uint8_t p2, std::string &response);

    /**
     * @brief Open a logical channel with the SE, selecting the applet represented by the given AID (when the AID is not
     * Null and the length of the AID is not 0).
     * @param aid The AID of the applet to be selected on this channel, as a byte array.
     * @param p2 The P2 parameter of the SELECT APDU executed on this channel.
     * @param apduResp The APDU response received.
     * @return An instance of channel. Null if the SE is unable to provide a new logical channel or is unable to
     * retrieve Access Control rules due to the lack of an available logical channel.
     */
    sptr<ChannelImpl> OpenLogicalChannel(const std::string &aid, uint8_t p2, std::string &response);

    sptr<ChannelImpl> OpenLogicalChannelWithoutChannelAccess(const std::string &aid, uint8_t p2, std::string &response);
    /**
     * @brief Transmit an APDU command (as per ISO/IEC 7816) to the SE.
     * @param command The APDU command to be transmitted, as a byte array.
     * @return The response received, as a byte array. The returned byte array contains the data bytes in the following
     * order: {<first data byte>, …, <last data byte>, <sw1>, <sw2>}.
     */
    std::string Transmit(const std::string &command);

    /**
     * @brief Call HDF to close specific channel for this secure element.
     * @param channelNum The channel number to be closed.
     * @param isBasicChannel Is basic chennel or not.
     * @param callingUniqueTag who call CloseChannel.
     */
    void CloseChannel(uint8_t channelNum, bool isBasicChannel, const std::string &callingUniqueTag);

    /**
     * @brief Close all chennels for this secure element. Call CloseChannel(channel) to close each channel.
     * @param callingUniqueTag who call CloseChannels.
     */
    void CloseChannels(const std::string &callingUniqueTag);

    /**
     * @brief Call HDF to reset the secure element.
     * @return True reset success, otherwise return false.
     */
    bool Reset();

    /**
     * @brief Handle the secure element state changed from hdf.
     * @param connected True the secure element is connected, false otherwise.
     */
    void OnStateChange(bool connected);

    /**
     * @brief Release the channel app opendn
     * @param callingUniqueTag who call se.
     */
    void ReleaseByCallingTokenId(const std::string &callingUniqueTag);
private:
    bool EseInitialize();
    bool SimInitialize();
    void EseResponseCheckAndHandle(std::vector<uint8_t> &vecCommand, std::vector<uint8_t> &vecResponse,
        EseSecureElementStatus &status);
    void SimResponseCheckAndHandle(std::vector<uint8_t> &vecCommand, std::vector<uint8_t> &vecResponse,
        SimSecureElementStatus &status);
    static void AddHdiDeathRecipient();
    static void RemoveHdiDeathRecipient();
    void CloseChannelCallBack();
    void CreatCloseChannelTimer();
    void SelectDefaultApplication();
    int32_t SelectCmd(const std::vector<uint8_t> &aid);
    int32_t ChannelPreProcess(uint8_t channelNum, bool isBasicChannel);
    bool IsBasicChannelBusy();
    bool CheckChannelAccess(const std::string &aid);
    bool OpenBasicChannelForEse(const std::string &aid, uint8_t p2, std::string &response);
    bool OpenBasicChannelForSim(const std::string &aid, uint8_t p2, std::string &response);
    bool OpenLogicalChannelForEse(const std::string &aid, uint8_t p2, uint8_t &channelNumber, std::string &response);
    bool OpenLogicalChannelForSim(const std::string &aid, uint8_t p2, uint8_t &channelNumber, std::string &response);
    uint8_t GetSlotIdBySimName();
    void SetConnectFlag(bool connected);
    bool GetConnectFlag();
private:
    // the secure element is connected or not.
    std::atomic<bool> secureElementEseConnected_ = false;
    std::atomic<bool> secureElementSim1Connected_ = false;
    std::atomic<bool> secureElementSim2Connected_ = false;

    // the name of this secure element.
    std::string seName_{};

    // the mutex lock to access the secure element hdf
    std::mutex mutex_{};

    // HDI interface to access the secure element hdf
    OHOS::sptr<ISeEseHdiV1_0> seEseHdiInterface_{};
    OHOS::sptr<ISeSimHdiV1_0> seSimHdiInterface_{};

    // Callback used to register to the secure element hdf.
    OHOS::sptr<EseISecureElementCallback> seEseHdfCallback_{};
    OHOS::sptr<SimISecureElementCallback> seSimHdfCallback_{};

    // all open chnannels for this secure element (from all session), channel id <--> channel instance mapping.
    std::map<std::string, std::map<uint8_t, sptr<ChannelImpl>>> openChannels_{};

    // access control enforcer used to do access control.
    std::shared_ptr<AccessControlEnforcer> accessControlEnforcer_{};
    static uint32_t closeChannelTimerId;
};
}  // namespace SE
}  // namespace OHOS
#endif // !SECURE_ELEMENT_AGENT_H